/**
 * FileFind - ftp/shares indexing and search php engine
 * Module: agent-java
 * Description: Agent written in java language
 * Copyright (c) 2003-2010 Rage Steel, www.gt2.ru
 */
package ru.ffind.agent.db;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import ru.ffind.common.model.Entry;
import ru.ffind.common.model.Filename;
import ru.ffind.agent.model.ListingStorage;
import ru.ffind.agent.sync.Sync;
import ru.ffind.agent.sync.SyncActions;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.Lists;
import ru.ffind.agent.dao.Dao;
import ru.ffind.agent.dao.DeleteFileBatch;
import ru.ffind.agent.dao.InsertFileBatch;
import ru.ffind.agent.dao.UpdateFileBatch;
import ru.ffind.common.model.Directory;

/**
 * Database actions that required for synchronizing
 *
 * @author Rage Steel
 */
public class DbSyncActions implements SyncActions {

	private final DbHost host;
	private final ListingStorage sourceListing;
	private final ListingStorage targetListing;
    private final Dao dao;
	
	public DbSyncActions(ListingStorage sourceListing, ListingStorage targetListing, Dao dao, DbHost host) {
		super();
		this.sourceListing = sourceListing;
		this.targetListing = targetListing;
        this.dao = dao;
		this.host = host;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void createDir(String absolutePath, Directory targetParent, Directory sourceDir) {
		doCreateDir(absolutePath, targetParent, sourceDir);
	}

	private DbDirectory doCreateDir(String absolutePath, Directory targetParent, Directory sourceDir) {
//		System.out.println("doCopyDir: " + absolutePath);
        DbDirectory targetParentDb = (DbDirectory)targetParent;
		DbDirectory newDirectory = dao.createDir(
                targetParentDb.getHostId(), targetParentDb, sourceDir);

		if (sourceDir.isError()) {
			// We have nothing to do with errors
			return newDirectory;
		}

		// Add files and directories
		ImmutableCollection<Entry> entries = sourceDir.getEntries();
		if (null == entries) {
			throw new IllegalStateException("Source dir must not be a placeholder!");
		}

		long totalSize = 0;
		int filesCount;
		int dirsCount;

		List<Directory> dirs = Lists.newArrayList();

		// Add files first
		InsertFileBatch fileBatch = dao.createInsertFileBatch();
		for (Entry entry: entries) {
			if (entry instanceof Filename) {
				Filename filename = (Filename)entry;
				totalSize += filename.getFileSize();

				// Add filename to database
                fileBatch.insertFilename(newDirectory, filename);
			} else if (entry instanceof Directory) {
				dirs.add((Directory)entry);
			} else {
				throw new IllegalArgumentException("Invalid entry class: " + entry.getClass());
			}
		}

		filesCount = fileBatch.executeBatch();

		// Then start adding directories
		dirsCount = dirs.size();

		for (Directory dir: dirs) {
			String dirAbsPath = dir.getAbsPath(absolutePath);

			// We need to get a directory instead of placeholder
			dir = sourceListing.getAndRemoveWithWait(dirAbsPath, Sync.RETRY_TIMEOUT);
			if (null == dir) {
				throw new IllegalArgumentException("Source directory not found, even a placeholder: " + dirAbsPath);
			}

			// Add directory to database
			DbDirectory subDir = doCreateDir(dirAbsPath, newDirectory, dir);

			filesCount += subDir.getFilesCount();
			dirsCount += subDir.getDirsCount();
			totalSize += subDir.getTotalSize();
		}

		newDirectory.setFilesCount(filesCount);
		newDirectory.setDirsCount(dirsCount);
		newDirectory.setTotalSize(totalSize);
		dao.updateDirStats(newDirectory);

		// Set root_dir_id for the host if new root directory created
		if (null == targetParent) {
			// FIXME Host must be updated at the end of scanning!
			dao.updateHostRootDirId(host, newDirectory);
		}
		return newDirectory;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeDir(String absolutePath, Directory dir) {
		// Delete directory contents

		// Add files and directories
		ImmutableCollection<Entry> entries = dir.getEntries();
		if (null == entries) {
			throw new IllegalStateException("Source dir must not be a placeholder!");
		}

		for (Entry entry: entries) {
			if (entry instanceof Directory) {
				String dirAbsPath = entry.getAbsPath(absolutePath);
				Directory subDir = targetListing.getAndRemoveWithWait(dirAbsPath, Sync.RETRY_TIMEOUT);
				if (null == subDir) {
					throw new IllegalArgumentException("Subdirectory of directory be removed not found: " + dirAbsPath);
				}
				removeDir(dirAbsPath, subDir);
			}
		}
		
		// Then delete directory itself
		dao.deleteDir((DbDirectory)dir);
	}


	/**
	 * {@inheritDoc}
	 */
	@Override
	public void editDir(Directory targetDir, Directory sourceDir) {
        dao.updateDir((DbDirectory)targetDir, sourceDir);
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void createFiles(Collection<Filename> files, Directory parent) {
        InsertFileBatch fileBatch = dao.createInsertFileBatch();
		for (Filename filename: files) {
			// Add filename to database
            fileBatch.insertFilename((DbDirectory)parent, filename);
		}

        fileBatch.executeBatch();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void editFiles(Map<Filename, Filename> files) {
        UpdateFileBatch updateFileBatch = dao.createUpdateFileBatch();
		for (Map.Entry<Filename, Filename> updateEntry: files.entrySet()) {
			Filename sourceFile = updateEntry.getKey();
			DbFilename dbFile = (DbFilename) updateEntry.getValue();
            updateFileBatch.updateFilename(dbFile, sourceFile);
		}
        updateFileBatch.executeBatch();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeFiles(Collection<Filename> files) {
		DeleteFileBatch deleteFileBatch = dao.createDeleteFileBatch();
		for (Filename filename: files) {
            deleteFileBatch.deleteFilename((DbFilename)filename);
		}
        deleteFileBatch.executeBatch();
	}

}
